package agend.behaviours.n_to_n;

import jade.core.AID;
import jade.core.behaviours.Behaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import agend.agents.AgendAgent;
import agend.dlv.DlvMaker.TypeOutput;
import agend.util.PerfomanceMeasure;
import dto.utils.DTONN;
import dto.utils.DataTransferObject;
import dto.utils.SingleEvent;

public class N_to_N_SenderBehaviour extends Behaviour {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private DataTransferObject dto;

	private MessageTemplate messageTemplate;
	private int step;

	private int numPartecipants;
	private int count;

	private String[] sortedPartecipants;

	private SingleEvent result;
	private Map<Integer, SingleEvent> resultMap;

	private List<String> partecipants;

	public N_to_N_SenderBehaviour(DataTransferObject dto) {
		
		PerfomanceMeasure.updateCountTentativi();

		this.dto = dto;
		result = new SingleEvent();
		resultMap = new HashMap<Integer, SingleEvent>();
		step = 0;
		partecipants = new ArrayList<String>();
	}

	@SuppressWarnings("unchecked")
	@Override
	public void action() {


		// TODO Auto-generated method stub
		switch (step) {

		case 0:
		{	
			List<SingleEvent> myEvents=((AgendAgent)myAgent).getGoogleServices().getEventsByRange(dto.getStartTime(), dto.getEndTime());

			dto.setEvents(myEvents);

			step = 1;

			break;
		}
		case 1:
		{
			ACLMessage message = new ACLMessage(ACLMessage.CFP);

			message.setSender(myAgent.getAID());

			for(String receiver : dto.getReceivers()) {

				message.addReceiver(new AID(receiver, AID.ISLOCALNAME));
			}

			message.setConversationId("N-to-N");
			message.setReplyWith("cfp"+System.currentTimeMillis());

			messageTemplate = MessageTemplate.and(MessageTemplate.MatchConversationId("N-to-N"),
					MessageTemplate.MatchInReplyTo(message.getReplyWith()));

			try {
				message.setContentObject(dto);
				myAgent.send(message);

				step = 2;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			numPartecipants=dto.getReceivers().size();
			sortedPartecipants=new String[numPartecipants];
			count=0;

			break;
		}
		case 2:
		{			
			ACLMessage response = myAgent.receive(messageTemplate);			

			if(response!=null)
			{

				sortedPartecipants[count]=response.getSender().getLocalName();	

				try {

					DTONN dtonn_tmp = (DTONN) response.getContentObject();

					((AgendAgent)myAgent).getDlvMaker().N_To_N_factsWriter(dtonn_tmp, count, count==numPartecipants-1, dto.getMin(), dto.getMax());

				} catch (UnreadableException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				count++;
			}
			else
				block();				

			if(count==numPartecipants)
				step=3;

			break;		
		}
		case 3:
		{
			((AgendAgent)myAgent).getDlvMaker().setSettings(dto.getStartTime(), dto.getEndTime(), null, null, null, 0);

			Object object = ((AgendAgent)myAgent).getDlvMaker().execute(TypeOutput.N_TO_N_RESULT);

			if(object instanceof Map) {

				resultMap = (Map<Integer, SingleEvent>) object;
			}

			step = 4;
			break;
		}
		case 4:
		{
			Iterator<Integer> iterator = resultMap.keySet().iterator();

			if(resultMap.keySet().iterator().hasNext())
				result = resultMap.get(resultMap.keySet().iterator().next());

			while(iterator.hasNext())
			{
				int index = iterator.next();
				partecipants.add(sortedPartecipants[index]);
			}

			if(result.isValid())
				step = 5;
			else{
				
				PerfomanceMeasure.getPerformanceMeasure();
				step=7;
			}
			break;
		}
		case 5:
		{
			DataTransferObject dataTransferObject = new DataTransferObject();
			dataTransferObject.setStartTime(result.getStartTime());
			dataTransferObject.setEndTime(result.getEndTime());

			ACLMessage message = new ACLMessage(ACLMessage.INFORM);

			message.setSender(myAgent.getAID());

			for(String partecipant : partecipants) {

				message.addReceiver(new AID(partecipant, AID.ISLOCALNAME));
			}

			message.setConversationId("IS-FREE");
			message.setReplyWith("info"+System.currentTimeMillis());

			messageTemplate = MessageTemplate.and(MessageTemplate.MatchConversationId("IS-FREE"),
					MessageTemplate.MatchInReplyTo(message.getReplyWith()));

			try {
				message.setContentObject(dataTransferObject);
				myAgent.send(message);

				step = 6;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}			

			count=0;

			break;
		}
		case 6:
		{
			ACLMessage replay = myAgent.receive(messageTemplate);

			if(replay!=null)
			{
				if(replay.getPerformative() == ACLMessage.CONFIRM)
				{
					count++;

					if(count==partecipants.size())
					{
						List<SingleEvent> myEvents=((AgendAgent)myAgent).getGoogleServices().getEventsByRange(result.getStartTime(), result.getEndTime());

						if(myEvents.isEmpty() || myEvents==null)
						{
							String id = ((AgendAgent)myAgent).getGoogleServices().setEvent(dto.getTitle(), dto.getDescription(), dto.getWhere(), result, partecipants, dto.getTimeZone());
							PerfomanceMeasure.getPerformanceMeasure(partecipants.size(), result.getPunteggio(), dto.getStartTime(), dto.getEndTime(), result.getStartTime());

							((AgendAgent)myAgent).addEventsToMap(id, dto);
						}
						else
							resetAppointment();

						step=7;
					}					
				}
				else if(replay.getPerformative() == ACLMessage.FAILURE)
				{
					resetAppointment();

					step=7;
				}
			}
			else
				block();

			break;
		}
		default:
			break;
		}

	}

	private void resetAppointment(){

		N_to_N_SenderBehaviour n_to_N_SenderBehaviour = new N_to_N_SenderBehaviour(dto);
		myAgent.addBehaviour(n_to_N_SenderBehaviour);
	}

	@Override
	public boolean done() {
		// TODO Auto-generated method stub
		return (step==7);
	}

}
